package com.tanhua.server.serivce;

import cn.hutool.core.collection.CollUtil;
import com.tanhua.dubbo.api.TestSoulApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.model.db.UserInfo;
import com.tanhua.model.mongo.*;
import com.tanhua.model.vo.*;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class TestSoulService {

    @DubboReference
    private TestSoulApi testSoulApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    /**
     * 问卷列表的展示
     *
     * @return
     */
    public List<PageVo> showPaperList() {

        //1、获取当前用户的id
        Long userId = UserHolder.getUserId();

        //2、查询所有的试卷  存放的是所有的试卷
        List<TestPaper> paperList = testSoulApi.getPages();

        //3、查询所有的问题
        Map<String, TestQuestion> questionMap = testSoulApi.getQuestions();
        //3.1  keySet方法 返回这个map集合所有的键名  无序  这里就是问题的id
        Set<String> questionIds = questionMap.keySet();

        //4、查询所有的选项  通过问题的id去查询每个问题的选项 map<问题的id,这个问题对应的所有的选项>
        Map<String, List<TestOptionVo>> optionsMap = testSoulApi.getOptions(questionIds);

        //5、查询用户的报告  根据用户的id进行查询
        List<Report> reportList = testSoulApi.findAllReportByUserId(userId);

        //6、进行封装数据   构造返回值PageVo
        //6.1  初始化一个集合  保存存放返回的对象
        List<PageVo> vos = new ArrayList<>();
        //6.2  遍历试卷的集合  为每个试卷填充数据
        for (TestPaper testPaper : paperList) {
            //6.3 返回的对象里有一个question集合   包含了所有的问题
            //6.3.1  构造一个questionVo的返回对象
            List<TestQuestionVo> testQuestionVos = new ArrayList<>();
            //6.3.2  遍历这个question的id的集合
            for (String questionId : questionIds) {
                //6.3.3  获取到问题
                TestQuestion question = questionMap.get(questionId);
                //6.3.4  对这个问题进行选项的装配
                List<TestOptionVo> testOptionVos = optionsMap.get(questionId);
                //获取到当前是那个试卷  枚举：1 2 3
                String paperId = question.getPaperId();
                //6.3.5  将装配好的问题和选项装配进试卷里面
                if (testPaper.getId().toString().equals(paperId)) {
                    //6.3.6 构造对应的QuestionVo对象  就是将问题和对应的选项封装进去
                    TestQuestionVo vo = TestQuestionVo.init(question, testOptionVos);
                    testQuestionVos.add(vo);
                }
            }

            //7、1  进行试卷的封装
            PageVo vo = PageVo.init(testPaper, testQuestionVos);

            //7、2  查询用户是否有报告
            //7.2.1 获取到到当前试卷的id
            String paperId = testPaper.getId().toString();
            //7.2.2 根据当前试卷的id和用户的id去查询用户是否有已经生成的报告
            Report report = testSoulApi.findReportByUserIdAndPaperId(paperId,userId);
            //7.2.3 如果返回的对象不为空  那么就将的得到的reportId添加到返回的对象中
            if (report != null) {
                vo.setReportId(report.getId().toString());
            }

            //8、1 判断锁的状态
            if ("初级测试题".equals(testPaper.getLevel().trim())) {
                vo.setIsLock(0);
            }
            if ("中级测试题".equals(testPaper.getLevel().trim()) && reportList.size()>=1){
                vo.setIsLock(0);
            }
            if ("高级测试题".equals(testPaper.getLevel().trim()) && reportList.size()>=2){
                vo.setIsLock(0);
            }
            vos.add(vo);
        }
        return vos;
    }

    /**
     * 问卷的提交
     * @param answers
     * @return
     */
    public String putAnswers(List<Map<String, String>> answers) {
        //创建一个用户的回答的集合
        List<String> answerList=new ArrayList<>();
        //当前问卷的id  枚举： 1 2 3
        String pageId=null;
        for (Map<String, String> answer : answers) {
            //获取前端传递过来的参数
            String questionId = answer.get("questionId");
            String optionId = answer.get("optionId");
            //根据前端传递过来的问题的id和选项的编号查询对应的问题和选项
            TestQuestion question = testSoulApi.getQuestionById(questionId);
            TestOption option = testSoulApi.getOptionById(optionId);

            //判断返回的问题和option是不是空的
            if (question==null||option==null){
                break;
            }

            //去查询选项表  看看用户选择的维度是什么
            String nature = option.getNature();
            //将维度作为用户的答案  存入到集合中
            answerList.add(nature);
            //获取到问卷的id
            pageId = question.getPaperId();
        }
        //获取当前用户的id
        Long userId = UserHolder.getUserId();
        //填充report
        Report report = new Report();
        report.setUserId(userId);
        report.setPageId(pageId);
        report.setAnswer(answerList);

        //如果用户之前之前已经提交过报告 那么就删除 从新保存
        Report hasRepot= testSoulApi.findReportByUPId(userId,pageId);

        //判断用户之前是否已经存在报告
        if (hasRepot!=null){
            testSoulApi.deleteReport(hasRepot);
        }

        //保存这个报告并且返回这个报告的id
        String reportId = testSoulApi.saveReport(report);

        return reportId;
    }

    /**
     * 查询用户的报告
     * @param reportId
     * @return
     */
    public ReportVo getReport(String reportId) {

        //1.得到报告  确定的条件userId 和  报告的id
        Long userId = UserHolder.getUserId();
        Report report = testSoulApi.findReportById(userId,reportId);
        //2.分析报告的答案
        //2.1 获取到当前用户的回答
        List<String> answer = report.getAnswer();

        Map<String,Integer> map=new HashMap<>();
        //3.判断用户得分的范围
        int score=0;
        for (int i = 0; i < answer.size(); i++) {
            if ("判断".equals(answer.get(i))){
                score+=5;
            }else if ("抽象".equals(answer.get(i))){
                score+=6;
            }else if ("理性".equals(answer.get(i))){
                score+=7;
            }else if ("外向".equals(answer.get(i))){
                score+=7;
            }
            else {
                score+=2;
                answer.set(i,"其他");
            }

            //分拣到map
            if (!map.containsKey(answer.get(i))){
                map.put(answer.get(i),0);
            }
            map.put(answer.get(i),map.get(answer.get(i))+1);
        }
        //4.提交维度
        List<DimensionsVo>  dimensionsVos=new ArrayList<>();
        //4.1封装
        Set<String> keySet = map.keySet();
        for (String scoreKey : keySet) {
            Integer scoreValue = map.get(scoreKey);
            DimensionsVo dimensionsVo = new DimensionsVo();
            //维度  枚举 ： 理性  外向  判断  抽象
            dimensionsVo.setKey(scoreKey);
            //维度值  是一个百分比
            dimensionsVo.setValue((scoreValue*100/10)+"%");
            //添加到vo对象中
            dimensionsVos.add(dimensionsVo);
        }

        //System.out.println(score);;
        //5.根据analysisId查询所有报告
        //5.1 根据分数获得对应的性格的表的对象
        Analysis analysis = testSoulApi.findAnaByScore(score, report.getPageId());
        if (analysis==null){
            throw new BusinessException(ErrorResult.error());
        }

        //5.2 获取到当前报告  将对应的分析类型的id塞进报告里
        report.setAnalysisId(analysis.getId().toString());
        //5.3 从新保存一下这个报告
        testSoulApi.saveReport(report);

        //5.4 根据获取到的性格表的id  去查询所有的符合自己类型的报告
        List<Report> reportList= testSoulApi.findByAnalysisId(analysis.getId().toString());
        // 查询除自己的所有好友id
        List<SimilarYouVo> similarYouVos=null;
        if (!CollUtil.isEmpty(reportList)){
            //6.查询report表中所有的用户信息
            List<Long> userIds = CollUtil.getFieldValues(reportList, "userId", Long.class);
            Map<Long, UserInfo> infoMap = userInfoApi.findByIds(userIds);
            //枚举 ： 1 2 3 4 用户的信息的id
            Set<Long> longSet = infoMap.keySet();
            similarYouVos=new ArrayList<>();
            //排除自己
            for (Long aLong : longSet) {
                if (aLong.equals(userId)){
                    continue;
                }
                //构造 similarYouVo  相似的人的vo对象
                UserInfo userInfo = infoMap.get(aLong);
                SimilarYouVo similarYouVo=new SimilarYouVo();
                similarYouVo.setId(Integer.valueOf(aLong.toString()));
                similarYouVo.setAvatar(userInfo.getAvatar());
                similarYouVos.add(similarYouVo);
            }
        }
        //构造返回值的对象
        ReportVo vo = ReportVo.init(analysis, dimensionsVos, similarYouVos);
        return vo;

    }
}
